Activity 2

Domain-Driven Design: Tactical Modeling

Identify subdomains and bounded contexts for the Library Management System, then model Entities and Value Objects using PlantUML class diagrams.

⏱ Duration: 60 minutes #DDD #BoundedContext #PlantUML #Entities #ValueObjects

Activity Overview

This activity has two parts. In Part 1, you will study the full subdomain map of a Library Management System, understand why each subdomain is classified as Core, Supporting, or Generic, and see how subdomains map onto Bounded Contexts.

In Part 2, you will zoom into three Bounded Contexts , Member Management, Loan Management, and Collection Management: and perform tactical modeling: identifying Entities, Value Objects, and drawing PlantUML class diagrams. One example (Member Management) is already solved for you as a template.

What you will produce

A completed subdomain/context map (Part 1), and three PlantUML class diagrams for the Member, Loan, and Collection contexts (Part 2). These diagrams will be used directly in Activity 4, where you implement the domain and persistence layers.

Learning Goals

By the end of this activity, you should be able to:
  • Distinguish between a subdomain (problem space) and a bounded context (solution space).
  • Classify subdomains as Core, Supporting, or Generic and justify the classification.
  • Identify Entities and Value Objects within a bounded context.
  • Model inheritance between related Entities using a common supertype.
  • Draw a class diagram in PlantUML that reflects a domain model.
  • Justify design decisions such as identity, immutability, and aggregate boundaries.

Prerequisites

  • Chapter 2 , Domain-Driven Design Fundamentals (Entities, Value Objects, Aggregates, Bounded Contexts).
  • Access to a PlantUML renderer ( IntelliJ plugin, VS Code PlantUML extension, or plantuml.com online editor).
  • This is mainly a software modeling exercise.

Before You Start: Subdomain vs. Bounded Context

These two terms are often confused, but they describe different things:

Concept Space Definition
Subdomain Problem space A natural division of the business itself. It exists whether or not software is written. Classified as Core, Supporting, or Generic.
Bounded Context Solution space An explicit boundary in the software model where the Ubiquitous Language has one precise meaning. Ideally maps 1:1 to a subdomain.
Key Idea

The word "Book" refers to the same real-world concept, but it means something different depending on the context. In the Collection Context, a Book is a rich object with ISBN, author, and shelf status. In the Loans Context, a Book is just a BookId reference. Same subdomain concept, different bounded meaning.

What is PUML?

PlantUML (PUML) is a text-based tool for creating diagrams. You write a simple text description of your diagram, and PlantUML renders it as an image. It is widely used in software engineering to create UML diagrams, including class diagrams, sequence diagrams, and more.

Follow these to add PUML support in IntelliJ IDEA:

  1. Open IntelliJ IDEA and go to File > Settings (or IntelliJ IDEA > Preferences on macOS).
  2. In the Settings/Preferences dialog, navigate to Plugins.
  3. Click on the Marketplace tab and search for "PlantUML Integration".
  4. Click the Install button next to the plugin.
  5. Restart IntelliJ IDEA to activate the plugin.

Part 1: Subdomain and Bounded Context Map

The table below presents the full subdomain map for the Library Management System. Study each row and the reasoning behind its classification.

Subdomain Type Why
Loan Management (circulation) Core This is the actual value the library delivers. Lending policies, due dates, and overdue rules are where the business logic and differentiation live.
Collection Management (catalog) Supporting Necessary to support lending , you cannot lend what is not cataloged , but managing a catalog is not itself a competitive differentiator.
Member Management Supporting Needed to know who is allowed to borrow, but membership record-keeping is not unique to this business.
Notifications (due-date reminders, overdue alerts) Generic A solved problem. Could be outsourced entirely to an email/SMS provider.
Authentication & Access Control Generic Login and permissions are almost never custom-built , typically handled by Spring Security or an OAuth provider.
Reporting & Analytics Generic Usage statistics and popular-title reports are typically handled with off-the-shelf BI tools.
Fines & Payments Generic (arguably Supporting) Payment processing is commoditized (e.g., Stripe), even though the business rule of when a fine applies is closer to core logic. A good discussion point , classification can depend on business context.
Library Management System Context Map
Figure A2.1 Library Management System Context Map
Discussion Point

Not every bounded context needs deep tactical modeling. Notifications, Authentication, and Reporting are thin wrappers around off-the-shelf capabilities , they do not need rich Entities or Aggregates. Rich tactical modeling is reserved for contexts tied to Core or Supporting subdomains, which is why Part 2 of this activity focuses only on Members, Loans, and Collection.

1 Task , Justify the Map

In pairs, answer the following in writing (3–4 sentences total):

  • Do you agree that Fines & Payments is Generic rather than Supporting? Defend your position.
  • Suppose the library wants to become known for an excellent personalized recommendation engine, meaning it will use machine learning to suggest books to members based on their borrowing history. Which subdomain would move to Core, and why?

Part 2: Tactical Modeling with PlantUML

We now zoom into the three Bounded Contexts most relevant to lending: Member Management, Loan Management, and Collection Management. For each, you will identify the Entities and Value Objects, then express them as a PlantUML class diagram.

Context Aggregate Root Modeling Challenge
Member Management Member Solved for you, use as the template.
Loan Management Loan Single Entity with supporting Value Objects, reinforces the pattern.
Collection Management LibraryItem (abstract) Multiple concrete Entities (Book, AudioMaterial, VideoMaterial) sharing a common supertype , introduces generalization.

1 Worked Example: Member Management Context

The Member Management context is fully solved below. Study it carefully. You will follow the same reasoning for Loan and Collection. You can find the PlantUML code that draws the UML diagram for the Member Management Context written in Appendix A.

ElementKindReasoning
MemberEntity (Aggregate Root)Has a persistent identity (memberId) that matters regardless of attribute changes (e.g., name change, tier upgrade).
MemberIdValue ObjectNeeds generation logic (e.g. UUID) and validation (non-null, correct format). A raw String/Long cannot carry that behavior.
MemberNameValue ObjectComposed of firstName/lastName; validated (non-empty), immutable.
EmailValue ObjectDefined entirely by its string value; validated (format) and immutable.
MembershipTierValue Object (Enum-like)STANDARD or PREMIUM: a constrained classification, not a thing with identity.
activeLoanCountSimple attribute (int)No validation rules or independent behavior : just a counter the Entity manages internally.
statusSimple attribute (enum)A plain classification flag with no behavior of its own; kept as a raw enum rather than a VO wrapper.
registrationDateSimple attribute (LocalDate)No validation or behavior beyond being a date : a primitive type is sufficient.

Below is the UML diagram for the Member Management Context. You can find the PlantUML code that draws the UML diagram for the Member Management Context written in Appendix A.

DDD Diagram for Member Management Context
Figure A2.2 DDD Diagram for Member Management Context
Notice

Not every field became a Value Object. activeLoanCount, status, and registrationDate are simple attributes because they carry no validation rules or independent behavior : they are just data the Entity tracks. Compare this to MemberId and Email, which are promoted to Value Objects specifically because they need generation or validation logic. This is the tactical test to apply: does this field have rules or behavior of its own? If yes → Value Object (implemented as a POJO). If no → keep it a simple attribute.

Also Notice

The composition arrow (*--) shows that Member owns its Value Objects : they cannot exist independently outside a Member. Also notice that only Member is stereotyped <<Aggregate Root>>; the Value Objects are not entities and have no identity field of their own.

2 Your Task: Collection Management Context

The Collection Management context is more interesting: the library lends more than just books. It also lends audio and video materials, which share some common fields (title, publication year, status) but also have their own unique fields. So, how should we model this in a domain-driven way? Concepts that you have learned in OOP (inheritance, polymorphism) are useful here, but we must also consider DDD tactical modeling rules: identity, immutability, and aggregate boundaries. Instead of modeling three unrelated aggregates (Book, AudioMaterial, VideoMaterial), model a single Aggregate Root supertype called LibraryItem, with three concrete subtypes. We will also make LibraryItem abstract, so that students cannot instantiate a generic library item that is neither a book, an audio item, nor a video. This is a common pattern in DDD: a supertype carries the shared fields and behavior, while each subtype adds only what makes it distinct.

This context is an example that has a mix of Entities and Value Objects. The table below lists the elements, their kind, and reasoning for each.

ElementKindNotes
LibraryItemEntity (abstract Aggregate Root)Common fields: itemId, title, status, publicationYear.
BookEntity (concrete subtype)Adds isbn, author.
AudioMaterialEntity (concrete subtype)Adds duration, narrator.
VideoMaterialEntity (concrete subtype)Adds duration, format, ageRating.
ISBN, Author, Duration, PublicationYear, MaterialFormatValue ObjectsShared or subtype-specific, all immutable.
Modeling Question

Why should LibraryItem be abstract rather than a concrete class students instantiate directly? Think about what it would mean to create a "generic" library item that is neither a book, an audio item, nor a video.

Key Idea

This is the same tactical modeling skill you used for Member and Loan, identify identity vs. attributes, but now applied across a family of related Entities. The supertype carries what all library items share; each subtype adds only what makes it distinct.

Build the UML diagram for this context using PUML following the example of the member context. Use <|-- for the generalization (inheritance) arrows.

3 Your Task: Loan Management Context

Using the reasoning from the Member Management example, analyze the elements below. Note how business invariants (e.g., maximum renewals allowed, valid date intervals, non-negative monetary amounts) determine whether an attribute is modeled as a simple primitive or encapsulated inside a Value Object.

Element Kind Reasoning (fill in or study)
Loan Entity (Aggregate Root) Possesses a unique lifecycle and identity (loanId). Enforces critical domain invariants across borrowing, renewals, and returns.
LoanId Value Object Needs format validation (e.g., non-null UUID/prefix) and factory generation logic.
borrowerId Simple attribute (MemberId reference) Cross-context reference to the borrowing Member. Held by identity only, Loan never holds the full Member object.
iemId Simple attribute (ItemId reference) Cross-context reference to the borrowed LibraryItem. Held by identity only, Loan never holds the full Book/LibraryItem object.
LoanPeriod Value Object Encapsulates temporal rules: dueDate must be after startDate, and calculates whether an item is currently overdue.
RenewalCount Value Object Guards the business invariant: a loan can be renewed at most 2 times. Increments itself and throws a domain exception if the threshold is exceeded.
Money Value Object Represents monetary amounts (e.g., daily overdue rates and accrued fines). Enforces non-negative values and safe arithmetic with currency.
status Simple attribute (enum) State flag (ACTIVE, RETURNED, OVERDUE, LOST) managed directly by the aggregate's transition methods.
Boundary Rule: Reference by Identity Only

Loan belongs to the Loan Management Bounded Context. It must never hold a direct reference to the full Member or Book/LibraryItem entity objects. Instead, it stores only their identity references (MemberId, ItemId).

Design Insight: Rich Invariants

Notice how modeling RenewalCount as a Value Object ensures that the rule "no loan can be renewed more than twice" is validated at the point of creation/increment, rather than relying on scattering raw int comparisons throughout your service layer.

Build the UML diagram using PUML as you did for Member and Collection. Use *-- for composition arrows and <|-- for generalization arrows.

Deliverables Checklist

Before You Finish , Check All Items

    Use the Forum on Moodle to answer this question:

  • Written justification for the Fines & Payments classification (Part 1).
  • Written answer identifying which subdomain would move to Core under the recommendation-engine scenario (Part 1).
  • Submit the PUML code and the rendered diagram on Moodle:

  • Completed PlantUML diagram for the Collection Management context, including generalization arrows.
  • Completed PlantUML diagram for the Loan Management context, rendered successfully.

Troubleshooting Guide

Problem Likely Cause Solution
PlantUML diagram does not render Missing @startuml/@enduml tags or a syntax typo. Check that both tags are present and every class block is closed with }.
Unsure if something is an Entity or a Value Object Confusing "has attributes" with "has identity." Ask: "Does it matter WHICH one it is, or just WHAT it is?" If which , Entity. If what , Value Object.
Tempted to give Loan a reference to the full Member object Forgetting the Bounded Context boundary. Replace it with MemberId. Cross-context references must be by ID only.
Not sure whether LibraryItem should be abstract Uncertainty about generalization in DDD. An abstract class cannot be instantiated on its own, which mirrors reality: every real item is specifically a Book, Audio, or Video , never a bare "LibraryItem."

Reflection Questions

Answer the following questions in your own words:

  1. What is the difference between a subdomain and a bounded context? Use one example from the Library system.
  2. Why are Notifications, Authentication, and Reporting modeled as "thin" bounded contexts rather than given rich domain models?
  3. Why does Loan reference BookId and MemberId instead of the full Book and Member objects?
  4. Why is LibraryItem modeled as an abstract Entity rather than a concrete one?
  5. Which Value Objects in your Loan and Collection diagrams could be reused across other contexts, and which are context-specific?

Up Next

Great work! You now have a complete subdomain/context map and three PlantUML class diagrams for the Members, Loans, and Collection contexts.

In Activity 3, you will bring these diagrams to life: implementing the Entities and Value Objects as plain Java domain classes, writing Repository interfaces for each Aggregate Root, and connecting them to a Spring Data JPA persistence layer, exactly as described in Chapter 2.

Appendix A: Member Management Context

Bellow is the PlantUML code for the Member Management Context diagram:


@startuml
title Member Management Context

skinparam class {
  BackgroundColor<> #FFE0B2
  BorderColor<> #E65100

  BackgroundColor<> #C8E6C9
  BorderColor<> #2E7D32

  BackgroundColor<> #B3E5FC
  BorderColor<> #01579B
}

class Member <> {
  - memberId : MemberId
  - name : MemberName
  - email : Email
  - tier : MembershipTier
  - status : MemberStatus
  - activeLoanCount : int
  - registrationDate : LocalDate
  --
  + canBorrow() : boolean
  + incrementLoanCount() : void
  + decrementLoanCount() : void
  + suspend() : void
  + reinstate() : void
}

class MemberId <> {
  - value : UUID
  --
  + generate() : MemberId
}

class MemberName <> {
  - firstName : String
  - lastName : String
  --
  + fullName() : String
}

class Email <> {
  - value : String
}

enum MembershipTier {
  STANDARD
  PREMIUM
}

enum MemberStatus {
  ACTIVE
  SUSPENDED
}

Member *-- MemberId
Member *-- MemberName
Member *-- Email
Member *-- MembershipTier
Member *-- MemberStatus

legend right
  |= Color |= Element |
  |<#FFE0B2> | **Aggregate Root** |
  |<#C8E6C9> | **Entity** |
  |<#B3E5FC> | **Value Object** |
  |<#F5F5F5> | **Enumeration** |
  --
  Aggregate Root:
  The entry point to an aggregate and the only
  object referenced from outside the aggregate.
  --
  Value Object:
  An immutable object defined by its values,
  without an independent identity.
endlegend

@enduml